-- SpellM
-- Create by panyl
-- 管理魔法卷轴的额外信息
-- 基本信息在ItemM中查询

module("SpellM", package.seeall);

-- 所有的魔法卷轴信息
local spells = {};

local skillMap = {};

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 转换下spell信息表
    local spell_CSV = get_spell_CSV();
    for i = 1, #spell_CSV do
        spells[spell_CSV[i].class_id] = spell_CSV[i];

        -- 技能id到卷轴id的映射
        skillMap[spell_CSV[i].cast_skill_id] = spell_CSV[i].class_id;
    end

    spell_CSV = {};
end

-- 模块初始化
function init()
    loadCsv();
end

-- 检索卷轴信息
function query(class_id, path)
    local m = spells[class_id]

    if not m or path == "" or not path then
        return m
    end

    return m[path]
end

-- 初始化卷轴
function initSpell()
    --根据当前英雄初始化卷轴 TODO
    local initSpells = ME.user.initSpells;

    if initSpells == nil then
        return
    end

    --添加到卷轴列表中
    local itemid = 0;
    for _, classId in initSpells do
        ItemM.addAmount(ME.user, classId, 1 );
    end
end

-----------------------------------------------------
-- 是否卷轴
-- @function [parent=#SpellM] isSpell
-- @param classId 道具ID
function isSpell(classId)
    if spells[classId] then
        return true;
    end

    return false;
end

-----------------------------------------------------
-- 使用卷轴
-- @function [parent=#SpellM] getTarget
-- @param classId 道具ID
function getTarget(classId)
    local skillId = query(classId, "cast_skill_id");

    -- 作用目标
    return SkillM.query(skillId, "target");
end

-- 根据技能id获取卷轴id
function getSpellId(skillId)
    return skillMap[skillId];
end

-- 计算施放卷轴的消耗
function calcSpellCost(classId)
    local skillId = SpellM.query(classId, "cast_skill_id");

    -- 获取卷轴需要的魔法消耗
    local requireMp = query(classId, "require_mp");

    -- 属性
    local prop;

    -- 百分比减少魔法消耗
    prop = PropM.combine(ME.user, "cost", "mp");
    local reduceMp = requireMp - PropM.apply(prop, requireMp);

    -- 蓝耗增加
    prop = PropM.combine(ME.user, "increased_mp_cost", 1);
    local addon = PropM.apply(prop, requireMp);

    -- 耗蓝量
    requireMp = requireMp + addon - reduceMp;

    -- 单体减消耗
    prop = PropM.combine(ME.user, "magic_cost", skillId);
    requireMp = PropM.apply(prop, requireMp);

    -- 系别减消耗
    local style = SkillM.query(skillId, "style");
    prop = PropM.combine(ME.user, "magic_cost_style", style);
    requireMp = PropM.apply(prop, requireMp);

    -- 减减减
    prop = PropM.combine(ME.user, "reduce_cost", "mp");
    requireMp = PropM.apply(prop, requireMp);

    -- 魔法值消耗加
    prop = PropM.combine(ME.user, "add_cost", "mp");
    requireMp = PropM.apply(prop, requireMp);

    return math.max(requireMp, 0);
end

-----------------------------------------------------
-- 使用卷轴
-- @function [parent=#SpellM] useSpell
-- @param classId 道具ID
-- @param pos 施放的目标格子编号(1-30)
function canUseSpell(classId, pos)
    if not DungeonM.checkDungeonAction() then
        -- 经检测，不允许执行地牢行为
        return false;
    end

    -- 检查是否拥有卷轴
    local skillId = SpellM.query(classId, "cast_skill_id");
    -- 元素球魔法不需要消耗卷轴
    if  not ElementMagicM.ownElementMagic(ME.user, skillId) and
        ItemM.getAmount(ME.user, classId) < 1 then
        return false;
    end

    -- 判断需求技能是否满足
    if true ~= FormulaM.invoke("CAN_USE_SPELL", classId) then
        return false;
    end

    -- 如果当前在副本中，需要额外判断一下
    if DungeonInstanceM.isInInstance() then
        if not (FormulaM.invoke("CAN_USE_SPELL_IN_INSTANCE", classId)) then
            local instanceInfo = DungeonInstanceM.getCurInstanceInfo();
            local msg = string.format(getLocStr("cannot_use_spell_in_instance"), instanceInfo["name"]);
            doAlert(msg);
            return false;
        end
    end

    -- TODO SLIMEC-426 临时方案：boss层无法使用大地之门
    if DungeonBossM.isInBossLayer() or ArenaM.isInArena() then
        if classId == 4005 then
            doAlert(getLocStr("cannot_use_spell_in_boss_layer"));
            return false;
        end
    end

    -- 获取卷轴需要的魔法消耗
    local requireMp = calcSpellCost(classId);

    -- 判断魔法值是否足够
    local curMp = ME.user:getMp();
    if curMp < requireMp then
        doAlert(getLocStr("no_enough_mp"));
        return false;
    end

    -- 这个技能是否能释放(像大地之门这类技能的判断)
    local ret = SkillM.canCast(ME.user, skillId, pos);
    if true ~= ret then
        if ret == "forbid_magic" then
            -- 处于禁魔状态
            doAlert(getLocStr("forbid_magic_tip"));
        elseif ret == "forbidden_magic" then
            -- 处于禁魔状态
            doAlert(getLocStr("forbidden_magic_tip"));
        elseif ret == "blood_poisoned" then
            -- 中了血毒
            doAlert(getLocStr("failed_reason_blood_poisoned"));
        elseif ret == "dark_ceremony" then
            -- 暗咒
            doAlert(getLocStr("failed_reason_dark_ceremony"));
        else
            -- 拦截后的提示
            local msg = FormulaM.invoke("CALC_FORBID_SKILL_ALERT", ret);
            if msg ~= "" then
                doAlert(msg);
            end
        end

        trace("SpellM", "经判断，不能使用卷轴%s。", query(classId, "desc") or "");
        return false;
    end

    return true;
end

-----------------------------------------------------
-- 使用卷轴
-- @function [parent=#SpellM] useSpell
-- @param classId 道具ID
-- @param pos 施放的目标格子编号(1-30)
function useSpell(classId, pos)
    if not canUseSpell(classId, pos) then
        return false;
    end

    local skillId = SpellM.query(classId, "cast_skill_id");

    -- 扣除卷轴
    local requireSpell = query(classId, "require_spell");
    -- 不是元素球魔法才需要扣除卷轴
    if  not ElementMagicM.ownElementMagic(ME.user, skillId) and
        not ItemM.costAmount(ME.user, classId, requireSpell) then
        cclog("cost amount fail");
        return false;
    end

    -- 扣除魔法值
    local requireMp = calcSpellCost(classId);

    ME.user:costAttrib("mp", requireMp);

    -- 如果是伤害类魔法
    local aliveCount = 0;
    if SkillM.query(skillId, "type") == USER_SKILL then
        -- 统计一下存活的怪物数量
        local aliveMonsters = FormulaM.getAliveMonsters(true);
        aliveCount = #table.keys(aliveMonsters);
    end

    -- 概率转化为其它技能
    -- 这里默认不会出现一个技能同时概率转化为两个技能的情况
    local props = PropM.fetchProps(ME.user, "spell_to_spell");
    for _, prop in ipairs(props) do
        if skillId == tonumber(prop[2]) then
            skillId = PropM.trigger(ME.user, prop[1], prop[2], 1);
        end
    end

    -- 施法受伤害
    props = PropM.fetchProps(ME.user, "spell_get_damage");
    for _, prop in ipairs(props) do
        PropM.trigger(ME.user, prop[1], prop[2]);
    end

    -- 使用卷轴收集冈布奥主动技能能量
    props = PropM.fetchProps(ME.user, "spell_gain_attrib");
    for _, prop in ipairs(props) do
        PropM.trigger(ME.user, prop[1], prop[2]);
    end

    -- 释放技能
    SkillM.cast(ME.user, skillId, pos);

    -- 远古记忆，提高魔法伤害，施法后触发
    props = PropM.fetchProps(ME.user, "ancient_memory");
    for _, prop in ipairs(props) do
        PropM.trigger(ME.user, prop[1], prop[2]);
    end

    -- 圣辉，使用卷轴加魔力，施法后触发
    props = PropM.fetchProps(ME.user, "spell_add_migic");
    for _, prop in ipairs(props) do
        if classId == tonumber(prop[2]) then
            PropM.trigger(ME.user, prop[1], prop[2]);
        end
    end

    -- 使用卷轴加生命，施法后触发
    props = PropM.fetchProps(ME.user, "spell_add_hp");
    for _, prop in ipairs(props) do
        if classId == tonumber(prop[2]) then
            PropM.trigger(ME.user, prop[1], prop[2]);
        end
    end

    -- 添加操作缓存
    DungeonM.addAction( { ["cmd"] = "use_spell", ["data"] = classId, ["pos"] = pos } );

    -- 如果是伤害类魔法
    local killCount = 0;
    if SkillM.query(skillId, "type") == USER_SKILL then
        -- 统计一下击杀的怪物数量
        local aliveMonsters = FormulaM.getAliveMonsters(true);
        killCount = aliveCount - #table.keys(aliveMonsters);
    end

    -- 抛出使用魔法卷轴事件
    EventMgr.fire(event.USE_SPELL, { ["skillId"] = skillId, ["killCount"] = killCount, });

    -- TODO: 如果是大地之门，这里特殊处理。没想到好办法，暂时猥琐点
    -- 大地之门的回合不抛出回合事件SLIMEC-4227
    if skillId == 5 then
        if not Socket.isConnected() then
            LoginM.tryReconnect(getLocStr("warning_title"), getLocStr("disconnect_tip"));
            LoginM.needSyncFlag(true);
            return true, true;
        end

        DungeonM.sync();

        EventMgr.fire(event.WAIT_LOADING);
    else
        -- 一个回合事件
        EventMgr.fire(event.COMBAT_ROUND, { ["pos"] = pos, ["isDelay"] = true, });
    end

    return true, true;
end

-----------------------------------------------------
-- 拆分卷轴
-- @function [parent=#SpellM] splitSpell
-- @param classId 卷轴的道具ID
function splitSpell(classId, count)
    if count == nil then
        count = 1;
    end

    if count > 65535 then
        count = 65535;
    end

    -- 检查是否拥有卷轴
    local amount = ItemM.getAmount(ME.user, classId);
    if amount < count then
        return false;
    end

    -- 扣除卷轴
    if not ItemM.costAmount(ME.user, classId, count) then
        cclog("cost amount fail");
        return false;
    end

    -- 获取拆分后能得到的魔法值
    local gainMp = FormulaM.invoke("CALC_SPLIT_SPELL_GAIN_MP", classId);
    gainMp = gainMp * count;

    -- 增加魔法值
    ME.user:addAttribBySplit("mp", gainMp);

    -- 额外增加属性
    local prop;
    local props = PropM.fetchProps(ME.user, "split_to_money");
    for _, prop in pairs(props) do
        BonusM.doBonus({ 2, prop[2], prop[3], }, "split_to_money");
    end

    -- 分解卷轴按阶位增加属性
    props = PropM.fetchProps(ME.user, "split_spell_attrib");
    for _, prop in pairs(props) do
        if prop[3] == query(classId, "rank") then
            PropM.trigger(ME.user, prop[1], prop[2], count);
        end
    end

    -- 拆分装备事件
    EventMgr.fire(event.SPLIT_SPELL, { classId = classId, gainMp = gainMp } );
    return true;
end

-----------------------------------------------------
-- 根据系别抽取卷轴
-- @function [parent=#SpellM] fetchSpell
-- @param style 系别
-- @param rand  随机数
function fetchSpell(style, rand)
    local arr = {};
    local sum = 0;

    for _, info in pairs(spells) do
        if info.style == style then
            table.insert(arr, info.class_id);

            sum = sum + info.weight;
        end
    end

    -- 排序一遍
    table.sort(arr);

    rand = rand % sum;
    sum = 0;
    for _, classId in pairs(arr) do
        sum = sum + spells[classId].weight;

        if sum > rand then
            return classId;
        end
    end
end

-- @function [parent=#SpellM] getSpellByRank
-- @param rank 阶位
function getSpellByRank(rank)
    local ret = {};

    for classId, data in pairs(spells) do
        if data.rank == rank and query(classId, "special_gain") ~= 1 then
            -- 特殊产出的就不用加进来了
            table.insert(ret, classId);
        end
    end

    -- 排序一遍
    table.sort(ret);

    return ret;
end

-- 根据阶位、系别抽取卷轴
-- @function [parent=#SpellM] fetchSpellByRank
-- @param rank 阶位
-- @param style 系别
function fetchSpellByRank(rank, style)
    local ret = {};

    for classId, data in pairs(spells) do
        if data.rank == rank and data.style == style then
            table.insert(ret, classId);
        end
    end

    -- 排序一遍
    table.sort(ret);

    return ret;
end

-------------------------------
-- 获取卷轴的描述
-- @param classId  卷轴id
function getSpellDesc(classId)
    local skillId = query(classId, "cast_skill_id");
    if not skillId then
        return "";
    end

    return SkillM.getSkillDesc(skillId);
end

-------------------------------
-- 判断能否合成卷轴
-- @param spellId  卷轴id
function canCompoundSpell(spellId, amount)
    local cost = FormulaM.invoke("CALC_COMPOUND_SPELL_COST", spellId);

    for k, v in pairs(cost) do
        local count = amount * v[3];
        if v[1] == 1 then
            if ItemM.getAmount(ME.user, v[2]) < count then
                return false;
            end
        elseif v[1] == 2 then
            if ME.user.dbase:queryAttrib(v[2]) < count then
                return false;
            end
        end
    end

    return true;
end

-------------------------------
-- 合成卷轴
-- @param spellId  卷轴id
-- @param amount   合成数量
function doCompoundSpell(spellId, amount)
    local cost = FormulaM.invoke("CALC_COMPOUND_SPELL_COST", spellId);

    if not canCompoundSpell(spellId, amount) then
        return false;
    end

    -- 先扣除材料
    for k, v in pairs(cost) do
        local count = amount * v[3];
        if v[1] == 1 then
            ItemM.costAmount(ME.user, v[2], count);
        elseif v[1] == 2 then
            if not ME.user:costAttrib(cost[2], cost[3]) then
                -- 扣除失败
                return false;
            end
        end
    end

    -- 卷轴合成
    local bonus = {1, spellId, amount};
    BonusM.doBonus(bonus, "do_spell_compound");

    -- 抛出卷轴合成事件
    EventMgr.fire(event.COMPOUND_SPELL, {["amount"] = amount, ["spell_id"] = spellId});
    return true;
end
